package jmine.tec.services.input.driver.xml;

import java.io.IOException;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import jmine.tec.datadigester.DataDigesterMessages;
import jmine.tec.services.api.ActionsEnum;
import jmine.tec.services.io.ServiceBean;
import jmine.tec.services.io.ServicesPageBean;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import bancosys.tec.datadigester.DigesterDriver;
import bancosys.tec.datadigester.DigesterDriverException;
import bancosys.tec.datadigester.DigesterFile;
import bancosys.tec.services.input.driver.bean.ServicePageHeader;

/**
 * Driver de XML de servico com tags.
 * 
 * @author Marcio Ribeiro (mmr)
 * @created Sep 9, 2011
 */
public class ServiceXmlTagDriver implements DigesterDriver<ServicePageHeader> {

    private static final String SERVICES_TAG = "services";

    /**
     * Trata XML de servico com tags.
     * 
     * @author Marcio Ribeiro (mmr)
     * @created Sep 9, 2011
     */
    private class ServiceSaxHandler extends DefaultHandler {
        private static final String DYN_TAG_NAME = "nome";

        private static final String DYN_TAG_VALUE = "valor";

        private ActionsEnum curAction;

        private ServiceBean curService;

        private ServicesPageBean curPage;

        private String curParam;

        private boolean isDynParam;

        private boolean isDynValue;

        private StringBuilder dynParam;

        private StringBuilder dynValue;

        private StringBuilder curValue;

        private ServicePageHeader importacao = new ServicePageHeader();

        /**
         * {@inheritDoc}
         */
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (this.curPage == null && SERVICES_TAG.equalsIgnoreCase(qName)) {
                this.newPage();
            } else if (this.curAction == null) {
                this.defineAction(qName);
            } else if (this.curService == null) {
                this.newService(qName);
            } else if (this.curParam != null) {
                if (qName.equalsIgnoreCase(DYN_TAG_NAME)) {
                    this.isDynParam = true;
                    this.dynParam = new StringBuilder();
                } else if (qName.equalsIgnoreCase(DYN_TAG_VALUE)) {
                    this.isDynValue = true;
                    this.dynValue = new StringBuilder();
                } else {
                    throw new IllegalStateException();
                }
            } else {
                this.curParam = qName;
                this.curValue = new StringBuilder();
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (this.curService != null && qName.equals(this.curService.getName())) {
                this.curService = null;
            } else if (this.curAction != null && qName.equalsIgnoreCase(this.curAction.name())) {
                this.curAction = null;
            } else if (this.curPage != null && qName.equalsIgnoreCase(SERVICES_TAG)) {
                this.curPage = null;
            } else if (this.curParam != null && qName.equals(this.curParam)) {
                this.handleBasicParam();
                this.handleDynParam();
            }
        }

        /**
         * Trata parametros basicos.
         */
        private void handleBasicParam() {
            if (this.curValue != null && this.curValue.length() > 0) {
                this.curService.getParameters().put(this.curParam, this.curValue.toString());
            }
            this.curValue = null;
            this.curParam = null;
        }

        /**
         * Trata parametros dinamicos.
         */
        private void handleDynParam() {
            if (this.dynParam != null && this.dynValue != null) {
                this.curService.getParameters().put(this.dynParam.toString(), this.dynValue.toString());
            }
            this.dynParam = null;
            this.dynValue = null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (this.curParam == null) {
                return;
            }

            String v = new String(ch, start, length);
            if (v == null || v.trim().length() == 0) {
                return;
            }

            if (this.isDynParam) {
                this.dynParam.append(v);
                this.isDynParam = false;
            } else if (this.isDynValue) {
                this.dynValue.append(v);
                this.isDynValue = false;
            } else {
                this.curValue.append(v);
            }
        }

        /**
         * Cria um novo servico.
         * 
         * @param n nome do servico.
         */
        private void newService(String n) {
            this.curService = new ServiceBean();
            this.curService.setName(n);
            this.curService.setAction(this.curAction);
            this.curPage.addService(this.curService);
            this.curValue = new StringBuilder();
        }

        /**
         * Define a acao corrente.
         * 
         * @param n nome da acao.
         */
        private void defineAction(String n) {
            for (ActionsEnum act : ActionsEnum.values()) {
                if (act.name().equalsIgnoreCase(n)) {
                    this.curAction = act;
                    return;
                }
            }
        }

        /**
         * Cria uma nova aba.
         */
        private void newPage() {
            this.curPage = new ServicesPageBean();
            this.importacao.addSheet(this.curPage);
        }

        /**
         * @return valor do campo importacao.
         */
        ServicePageHeader getImportacao() {
            return this.importacao;
        }
    }

    /**
     * {@inheritDoc}
     */
    public ServicePageHeader parse(DigesterFile file, Map<String, String> props) {
        try {
            ServiceSaxHandler handler = new ServiceSaxHandler();
            SAXParserFactory.newInstance().newSAXParser().parse(file.getInputStream(), handler);
            return handler.getImportacao();
        } catch (SAXException e) {
            throw new DigesterDriverException(DataDigesterMessages.GENERIC_PROCESSOR_ERROR.create(), e);
        } catch (IOException e) {
            throw new DigesterDriverException(DataDigesterMessages.GENERIC_PROCESSOR_ERROR.create(), e);
        } catch (ParserConfigurationException e) {
            throw new DigesterDriverException(DataDigesterMessages.GENERIC_PROCESSOR_ERROR.create(), e);
        }
    }

}
